package com.engine.cn.mq;

import jakarta.annotation.Resource;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * RabbitMQ如何保证消息不丢失？
 * Producer-->Exchange-->消息队列-->Consumer（类型）
 * 1)确保消息到MQ。
 * 2)确保消息路由到正确的队列
 * 3)确保消息在队列正确的存储
 * 4)确保消息从队列中正确的投递至消费者
 * 解决方案-->
 * 确保消息到MQ:发送方的确认模式。
 * 确保路由到:开启失败通知，并设置回调方法(写到数据库)
 * 确保消息队列正确的存储：交换器、队列、消息内容都需要持久化。
 * 确保消息从队列中正确的投递至消费者：消费者确认方法
 */
@Configuration
public class RabbitProducerConfig {

    @Resource
    private ConnectionFactory connectionFactory;

    // 定义队列
    @Bean
    public Queue demoQueue() {
        return new Queue("demo.queue", true); // true表示持久化
    }

    // 定义交换机
    @Bean
    public DirectExchange demoExchange() {
        return new DirectExchange("demo.exchange");
    }

    /**
     * 生产者配置(消息确认机制)
     *
     * @return RabbitTemplate
     */
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);

        //1.开启生产者确认模式(确保消息投送到mq中)，这里可以直接写个接口传入，接口中重新写实现类实现，这里为了简单这么写一下凑活
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            if (!ack) {
                System.err.println("消息发送失败: " + cause + "，需要重试或记录到数据库");
                // 这里可以实现重发逻辑或记录到数据库
            }
        });

        //2.开启return模式(消息无法路由到队列时回调,mandatory是委任统治的意思)
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setReturnsCallback(returned -> {
            System.err.println("消息未路由到队列: " + returned.getMessage());
            // 处理无法路由的消息，这里可以写表，也可以写日志
        });

        //3.设置消息持久化，持久化到硬盘（替代setDefaultDeliveryMode）
        rabbitTemplate.setBeforePublishPostProcessors(message -> {
            message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            return message;
        });

        return rabbitTemplate;
    }
}
